﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Xml.Linq;

namespace DBSchemaViewer.BackEnd.ModelXML
{
    /// <summary>
    /// Core for handling data, this class is a singleton and is not exposed outside the assembly.
    /// 
    /// This class is a singleton, we used a thread safe approach to manage it, case 4 of the following link:
    /// http://www.yoda.arachsys.com/csharp/singleton.html
    /// 
    /// 
    /// Ms Official documentation: 
    /// http://msdn.microsoft.com/en-us/library/ms998558.aspx
    /// 
    /// But the content of the class is not thread safe, if it needs to be use in a multithreaded environment
    /// you will need to add some locking.
    /// </summary>
    internal sealed class DataStoreXML
    {
        /// <summary>
        /// Singleton...
        /// </summary>
        static readonly DataStoreXML instance=new DataStoreXML();

        // Root of the data XML tree
        private XElement _root = null;
        private int _lastInternalID = 0;

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static DataStoreXML()
        {
        }

        DataStoreXML()
        {
        }


        public static DataStoreXML Instance
        {
            get
            {
                return instance;
            }
        }

        public XElement SettingsBranch
        {
            get
            {
                return _root.Element("header");
            }
        }

        public XElement RelationsBranch
        {
            get
            {
                return _root.Element("body").Element("relations");
            }
        }

        public XElement TablesBranch
        {
            get
            {
                return _root.Element("body").Element("tables");
            }
        }

        
        public int NextID
        {
            get
            {
                _lastInternalID++;
                _root.Element("header").Element("idseed").Attribute("lastid").SetValue(_lastInternalID);

                return _lastInternalID;
            }
        }

        public float CanvasSizeX
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }

        public int ScaleFactor
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }

        public void NewDocument()
        {
            // TODO: Move this to the service layer / Backend
            // Temporary implemented here !!! we have to define the service layer !!!
            // this should be called via web services
            //
            // It should return an string and then we build up the root element

            _root = new XElement("root",
                                        new XElement("header",
                                            new XElement("canvassize", new XAttribute("x", 2048), new XAttribute("y", 1200), new XAttribute("scalefactor", 1)),
                                            new XElement("idseed", new XAttribute("lastid", 1)),
                                            new XElement("dbengine", new XAttribute("type", "SQLSERVER"))
                                        ),
                                        new XElement("body",
                                            new XElement("tables"),
                                            new XElement("relations")
                                        )
                                    );

        }

        public string DocumentContent()
        {
            return (_root == null) ? string.Empty : _root.ToString();
        }

        /// <summary>
        /// When a table is moved, removed, or updated, we should update the relations that are surroinding
        /// the affected table.
        /// 
        /// This method will 
        /// </summary>
        /// <param name="tableID"></param>
        /// <returns></returns>
        public XElement[] SearchRelationsByTableID(int tableID)
        {
            XElement relationsRoot = RelationsBranch;
            XElement[] relations = null;

            if (relationsRoot != null)
            {


               relations = (from prelation in relationsRoot.Elements("relation")
                             where ((int)prelation.Element("tablefromid") == tableID ||
                                    (int)prelation.Element("tabletoid") == tableID
                                    )
                             select prelation).ToArray();
               
            }

            return relations;
        }


        public XElement[] SearchRelationsByTableID(int? tableOriginID, int? tableDestinationID)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// Search for a given table ID and returns the node that holds the information about
        /// that table.
        /// </summary>
        /// <param name="tableID"></param>
        /// <returns>The XElement node thata contains the table information or null if not found</returns>
        public XElement SearchTable(int tableID)
        {
            XElement tablesRoot = TablesBranch;
            XElement tableNode = null;

            tableNode = (from ptable in tablesRoot.Elements("table")
                              where (int)ptable.Attribute("id") == tableID
                              select ptable).First();
            
            return tableNode;
        }


        public void UpdateTable(int tableInternalID, XElement newUpdatedTableSubTree)
        {
            XElement oldTableSubtree = SearchTable(tableInternalID);

            if (oldTableSubtree != null)
            {                
                oldTableSubtree.Remove();
                TablesBranch.Add(newUpdatedTableSubTree);               
            }
        }

        public XElement SearchTableField(int tableID, int fieldID)
        {
            XElement fieldNode = null;
            XElement tableNode = SearchTable(tableID);
            
            if (tableNode != null)
            {
                fieldNode = (from pField in tableNode.Element("fields").Elements("field")
                             where fieldID == Convert.ToInt32(pField.Attribute("id").Value)
                             select pField).First();
            }

            return fieldNode;
        }

        /// <summary>
        /// Just remove the relation XML Node, doesn't check for references on other tables or whatever
        /// </summary>
        /// <param name="?"></param>
        public void DeleteRelation(int relationID)
        {            
            XElement relationToRemove = SearchRelation(relationID);

            if(relationToRemove != null)
                relationToRemove.Remove();

        }

        /// <summary>
        /// Just remove the table XML Node, doesn't check for references on other tables or whatever
        /// </summary>
        /// <param name="?"></param>
        public void DeleteTable(int tableID)
        {
            XElement TableToRemove = SearchTable(tableID);

            if (TableToRemove != null)
                TableToRemove.Remove();

        }



        public XElement[] ReturnAllTables()
        {
            XElement tablesRoot = TablesBranch;
            XElement[] tableNodes = null;

            tableNodes = (from ptable in tablesRoot.Elements("table")                         
                         select ptable).ToArray();

            return tableNodes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public XElement[] ReturnAllRelations()
        {
            XElement RelationsRoot = RelationsBranch;
            XElement[] RelationNodes = null;

            RelationNodes = (from ptable in RelationsRoot.Elements("relation")
                          select ptable).ToArray();

            return RelationNodes;

        }


        /// <summary>
        /// Returns an XElement node of a given relation (identified by it's unique id)
        /// </summary>
        /// <param name="relationID"></param>
        /// <returns></returns>
        public XElement SearchRelation(int relationID)
        {            
            XElement RelationNode = null;

            RelationNode = (from prelation in RelationsBranch.Elements("relation")
                            where (int)prelation.Attribute("id") == relationID
                            select prelation).First();

            return RelationNode;

        }

        public bool LoadDocument(string xmlContent)
        {
            _root = XElement.Parse(xmlContent);

            // Establish the next internal ID seed
            _lastInternalID = Convert.ToInt32(_root.Element("header").Element("idseed").Attribute("lastid").Value);

            // TODO: Add erorr handling
            return true;
        }


    }
}
